#include "Level.h"
#include "../GraphicManager.h"


const char lcWall = ' ';

// Function to initialize the level
bool cLevel::Init(string path) {
	
	mbMessageOn = false;
	ResetActionsExecuted();
	mDictionary.resize(0);
	mObjects.resize(0);
	mConversationManager.Init(kPathConversation);
	ReadLevel(path);
	
	return true;
}

// Function to uptade the level
void cLevel::Update(float lfTimeStep) {			
	if (mConversationManager.IsInConversation()) {
		mConversationManager.Update(lfTimeStep);
		return;
	}
	UpdateObjects(lfTimeStep);
	mRules.EvalRules();
}

// Function to render the level
void cLevel::Render() {
	if (mConversationManager.IsInConversation()) {
		mConversationManager.Render();
	}
		
	cGraphicManager & lGraphic = cGraphicManager::Get();
	lGraphic.SetColor(eBlack, eGrey);
	for (int i = 0; i < muiWidth; i++) {
      for (int j = 0; j < muiHeight; j++) {
         if (!mabMap[i + j * muiWidth]) {
				lGraphic.WriteChar(i, j, lcWall);
			}
		}
	} 
	lGraphic.SetColor(eWhite, eBlack);
	cSpecialCellDictionaryIt itDictionary;
	for (itDictionary = mDictionary.begin(); itDictionary != mDictionary.end(); itDictionary++) {
		if ((*itDictionary)->meCellContent == eSpecialLocation && !(*itDictionary)->mName.empty()) {
			lGraphic.WriteChar((*itDictionary)->miX, (*itDictionary)->miY, (*itDictionary)->mcSymbol);
		}
	}			  
	RenderObjects();

	if (mbMessageOn) {
		lGraphic.SetColor(eWhite, eBlack);
		int luiMessage_X = (int) ((muiWidth/2) - (mMessage.length() / 2));
		lGraphic.WriteChars(luiMessage_X, kfMessage_Y, mMessage.c_str());

		int luiActions = GetActionsExecuted();
		if (luiActions == 2) {
			SetMessageOn(false);
			ResetActionsExecuted();
		}
	}
}

// Function to deinitialize the level
bool cLevel::DeInit() {
	cSpecialCellDictionaryIt itDictionary = cSpecialCellDictionaryIt();
	for (itDictionary = mDictionary.begin(); itDictionary != mDictionary.end(); itDictionary++) {
		delete(*itDictionary);
	} 
	mDictionary.resize(0);
	cObjectListIt itObject = cObjectListIt();
	for (itObject = mObjects.begin(); itObject != mObjects.end(); itObject++) {
		delete(*itObject);
	}
	mObjects.resize(0);  
	mRules.ClearList();
	return true;
}

void cLevel::ReadMap(stringstream& mapBuffer, TiXmlNode* pParent) {
	TiXmlNode* pChild;
	TiXmlAttribute* pAttrib;
	mapBuffer << endl;
	// we write into the buffer "Level"
	mapBuffer << pParent->Value() << ": ";
	pAttrib = pParent->ToElement()->FirstAttribute();
	// we write in the flow the width and height

	pAttrib->QueryIntValue(&muiWidth);
	pAttrib->Next()->QueryIntValue(&muiHeight);
	mapBuffer << "(" << muiWidth << "x" << muiHeight << ")" << endl;
	mabMap = new bool[muiWidth * muiHeight];

	// we write all the map's rows
	int luiY = 0;
	for ( pChild = pParent->FirstChild()->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) {
		string lsRow = pChild->FirstChild()->Value();
		for (int luiX = 0; luiX < muiWidth; luiX++) {
			if (lsRow[luiX] == '#')
				mabMap[luiX + luiY * muiWidth] = false;
			else {
				if (lsRow[luiX] != ' ') {
					cSpecialCell *lpCell = new cSpecialCell;
					lpCell->mcId = lsRow[luiX];
					lpCell->miX = luiX;
					lpCell->miY = luiY;
					mDictionary.push_back(lpCell);
				}	
				mabMap[luiX + luiY * muiWidth] = true;
			}
		}
		mapBuffer << lsRow << endl;	
		luiY++;
	}
	mapBuffer << endl;
}

void cLevel::ReadSpecialCells(stringstream& cellsBuffer, TiXmlNode* pParent) {
	int liType = 0;
	TiXmlNode* pChild;
	TiXmlAttribute* pAttrib;
	cSpecialCellDictionaryIt it = mDictionary.begin();
	cellsBuffer << pParent->Value() << ":" << endl;
	for ( pChild = pParent->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) {
		int cont = 0;
		for ( pAttrib = pChild->ToElement()->FirstAttribute(); pAttrib != 0; pAttrib = pAttrib->Next()) {
			switch (cont) {
				case 0:	
					LookUp(pAttrib->Value()[0], it);
					cellsBuffer << "Cell[" << pAttrib->Value() << "] "; 
					break;
				case 1:	
					liType = GetSpecialType(pAttrib->Value());
					SetCellContent(it, liType);
					cellsBuffer << pAttrib->Value() << " ";
					break;
				case 2:	
					SetName(it, pAttrib->Value());
					cellsBuffer << "(Type:" << pAttrib->Value() << ") "; 
					break;
				case 3:	
					SetSymbol(it, pAttrib->Value()[0]);
					cellsBuffer << "Represented by: [" << pAttrib->Value() << "] "; 
					break;
			}
			cont++;
		}
		cellsBuffer << endl;
	}
	cellsBuffer << endl;
}

void cLevel::ReadRules(stringstream& rulesBuffer, TiXmlNode* pParent) {
	TiXmlNode* pChild;
	rulesBuffer << "Rules:" << endl;
	pParent = pParent->NextSibling();
	for ( pChild = pParent->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) {
		rulesBuffer << "If ";
		cLevelRule lRule;
		ReadConditions(rulesBuffer, pChild, lRule);
		ReadActions(rulesBuffer, pChild, lRule);
		mRules.AddRule(lRule);
	}
	rulesBuffer << endl;
}

void cLevel::ReadConditions(stringstream& condBuffer, TiXmlNode* pChild, cLevelRule& rule) {
	TiXmlNode* pGrandChild;
	TiXmlAttribute* pAttrib;
	cStatement cond;
	for ( pGrandChild = pChild->FirstChild()->FirstChild(); pGrandChild != 0; pGrandChild = pGrandChild->NextSibling()) {
		condBuffer << "(" ;
		pAttrib = pGrandChild->ToElement()->FirstAttribute(); 
		string lType = pGrandChild->Value();
		condBuffer << "Cell: " << pAttrib->Value();
		string lCellId = pAttrib->Value();
		pAttrib = pAttrib->Next();
		condBuffer << "==" << pAttrib->Value();
		string lObjectName = pAttrib->Value();
		condBuffer << ") " ;
		cond = InsertCondition(lType, lCellId, lObjectName);
		rule.AddConditionStatement(cond);
	}
	condBuffer << endl;
}

void cLevel::ReadActions(stringstream& condBuffer, TiXmlNode* pChild, cLevelRule& rule) {
	TiXmlNode* pGrandChild;
	TiXmlAttribute* pAttrib;
	cStatement action;
	vector<string> lParams;
	
	for ( pGrandChild = pChild->FirstChild()->NextSibling()->FirstChild(); pGrandChild != 0; pGrandChild = pGrandChild->NextSibling()) {
		lParams.resize(0);
		condBuffer << "(";
		string lType = pGrandChild->Value();
		condBuffer << pGrandChild->Value() << ":";

		for (pAttrib = pGrandChild->ToElement()->FirstAttribute(); pAttrib != 0; pAttrib = pAttrib->Next()) {
			lParams.push_back(pAttrib->Value());
			condBuffer << pAttrib->Value() << " ";
		}
		action = InsertAction(lType, lParams);
		rule.AddActionStatement(action);
		condBuffer << ")" << endl;
	}	  
}

void cLevel::ReadLevel(string path) {

	TiXmlDocument doc(path);
	bool lbLoadOk = doc.LoadFile();

	if (lbLoadOk) {
		OutputDebugString("XML Load: OK\n");
		TiXmlNode* pParent = &doc;
		stringstream strBuffer;

		// We access to the Level tag
		pParent = pParent->FirstChild()->NextSibling();
		// We read the map
		ReadMap(strBuffer, pParent);
		pParent = pParent->FirstChild()->NextSibling();
		// We read the Special cells
		ReadSpecialCells(strBuffer, pParent);
		// We read the rules
		ReadRules(strBuffer, pParent);
		cSpecialCellDictionaryIt it = mDictionary.begin();
		for (it = mDictionary.begin(); it != mDictionary.end(); it++) {
			InsertObject(it);
	}
		OutputDebugString(strBuffer.str().c_str());
	}
	else {
		OutputDebugString("XML Load: FAILED\n");
	}

}

bool cLevel::LookUp(char lcId, cSpecialCellDictionaryIt &it) {
	for (it = mDictionary.begin(); it != mDictionary.end(); it++) {
		if ((*it)->mcId == lcId) {
			return true;
		}
	}
	return false;
}

bool cLevel::LookUp(string lname, cObjectListIt &itObject) {
	for (itObject = mObjects.begin(); itObject != mObjects.end(); itObject++) {
		if ((*itObject)->GetName().compare(lname) == 0)	{
			return true;
		}
	}  
	return false;
}

void cLevel::Remove(string lname)  {
	cObjectListIt itObject;
	if (LookUp(lname,	itObject))
		DeleteEntry(itObject);
}

int cLevel::GetSpecialType(string name) {
	if (name.compare("Player") == 0)  
		return ePlayer;
	else if (name.compare("Object")== 0)
		return eObject;
	else if (name.compare("Special Location")== 0)
		return  eSpecialLocation;
	else 
		return NULL;
}


bool cLevel::IsWall(int luiX, int luiY) {
	return !(mabMap[luiX + luiY * muiWidth]);
}

void cLevel::InsertObject(const cSpecialCellDictionaryIt it) {
		cObject* lpObj = new cObject;
		int luiX;
		int luiY;
		string lName;
		char lSymbol;
		switch ((*it)->meCellContent) {
			case eObject:
				luiX = (*it)->miX;
				luiY = (*it)->miY;
				lName = (*it)->mName;
				lSymbol = (*it)->mcSymbol;
				lpObj->SetX(luiX);
				lpObj->SetY(luiY);
				lpObj->SetName(lName);
				lpObj->SetSymbol(lSymbol);
				mObjects.push_back(lpObj);
				break;
			case ePlayer:
				luiX = (*it)->miX;
				luiY = (*it)->miY;
				lName = (*it)->mName;
				lSymbol = (*it)->mcSymbol;
				lpObj = new cPlayer();
				lpObj->SetX(luiX);
				lpObj->SetY(luiY);
				lpObj->SetName(lName);
				lpObj->SetSymbol(lSymbol);
				mObjects.push_back(lpObj);
				break;
		}
	}	

void cLevel::RenderObjects() {
	cObjectListIt itObject;
	for (itObject = mObjects.begin(); itObject != mObjects.end(); itObject++) {
		(*itObject)->Render();
	} 
} 

void cLevel::UpdateObjects(float lfTimeStep) {
	cObjectListIt itObject;
	for (itObject = mObjects.begin(); itObject != mObjects.end(); itObject++) {
		(*itObject)->Update(lfTimeStep);
	} 
} 

bool cLevel::IsInCell(char lcId, string lname ) {
	cSpecialCellDictionaryIt it;
	int luiCellX;
	int luiCellY;

	if (LookUp(lcId, it)) {
		luiCellX = (*it)->miX;
		luiCellY = (*it)->miY;
	}
	else {
		 return false;
	}
	int luiObjectX;
	int luiObjectY;
	cObjectListIt itObject;
	if (LookUp(lname, itObject)) {
		luiObjectX = (*itObject)->GetX();
		luiObjectY = (*itObject)->GetY();
	}
	else {
		return false;
	}
	if (luiCellX == luiObjectX && luiCellY == luiObjectY)
		return true;
	return false;
}

cStatement cLevel::InsertCondition(string lType, string lCellId, string lObjectName) {
	cStatement lCond;
	lCond.SetType(lType);
	lCond.setParam1(lCellId);
	lCond.setParam2(lObjectName);
	return lCond;
}

cStatement cLevel::InsertAction(string lType, vector<string> lParam) {
	cStatement lAction;
	lAction.SetType(lType);
	switch (lParam.size()) {
		case  1:
			lAction.setParam1(lParam[0]);
			break;
		case 2:
			lAction.setParam1(lParam[0]);
			lAction.setParam2(lParam[1]);
			break;
	}
	return lAction;
}